PostgreSQL SQL优化 ROWNUM引发的全表扫描问题
1 背景知识
熟悉Oracle的DBA 应该很熟悉rownum
伪列,不恰当地使用rownum
,容易导致性能问题,包括影响索引使用,无法使用并行等。
同样的现象在KingbaseES 也是存在的。以下举例演示下。
2 无法使用索引的场景
2.1 环境准备
CREATE TABLE t01(id1 integer, id2 integer, name text);
INSERT INTO t01
SELECT GENERATE_SERIES(1,1000000),MOD(generate_series(1,1000000),500),REPEAT('a',1000);
CREATE INDEX idx_t01_id1 ON t01(id1);
2.2 构造查询
1、 (select id1, rownum from t01)
是一个 CTE 表达式,表达式中使用了rownum。
2、从下面结果得出rownum 为123,而不是1。
WITH tmp_query1 AS
(SELECT id1, rownum FROM t01)
SELECT * FROM tmp_query1 WHERE id1=123;
//屏幕输出:
id1 | rownum
-----+--------
123 | 123
(1 行记录)
2.3 查看执行计划
EXPLAIN WITH tmp_query1 AS
(SELECT id1, rownum FROM t01)
SELECT * FROM tmp_query1 WHERE id1=123;
//屏幕输出:
QUERY PLAN
--------------------
Subquery Scan on tmp_query1 (cost=0.00..162858.12 rows=1 width=12)
Filter: (tmp_query1.id1 = 123)
-> Count (cost=0.00..162858.12 rows=0 width=12)
-> Seq Scan on t01 (cost=0.00..152858.06 rows=1000006 width=4)
(4 行记录)
3 正常使用索引的场景
3.1 构造查询
从下面结果得出rownum 为1,是正常的输出的一种情况。
SELECT id1, rownum AS row_num FROM t01 WHERE id1=123;
3.2 查看执行计划
EXPLAIN SELECT id1, rownum AS row_num FROM t01 WHERE id1=123;
//屏幕输出:
QUERY PLAN
--------------
Count (cost=0.42..8.45 rows=0 width=12)
-> Index Only Scan using idx_t01_id1 on t01 (cost=0.42..8.44 rows=1 width=4)
Index Cond: (id1 = 123)
(3 行记录)
Note
Rownum 的结果是和执行计划无关,如果没有索引,也是1。
4 性能分析总结
1、 rownum
是当结果集查询出来之后,添加的一个伪列。
2、rownum
也会影响优化器对于子查询的提升。
(1)在不提升的情况下,优化器对于子查询先处理,访问表的全部数据,并添加伪列 rownum
。所以使用 CTE
表达式的那个例子rownum
返回 123 。
(2)如果优化器对于语句进行了提升和合并,那么 rownum
则为1。所以不使用 CTE
表达式的那个例子rownum
返回 1 。